use axum::{Json, Router, 
    extract::{Path, State}, 
    response::IntoResponse, 
    routing::{get, post}};
use serde::{Deserialize, Serialize};
use sqlx::{PgPool,FromRow};
use tokio::{
    net::{ TcpListener}
    };

use http::{HeaderMap, StatusCode, header::LOCATION};
use tracing::info;
use tracing_subscriber::{filter::LevelFilter, 
    fmt::Layer, 
    layer::SubscriberExt, 
    util::SubscriberInitExt, Layer as _};

use anyhow::Result;

use nanoid::nanoid;

#[derive(Debug, Deserialize)]
struct ShortenReq{
    url: String,
}

#[derive(Debug, Serialize)]
struct ShortenRes{
    url: String,
}


#[derive(Debug, FromRow)]
struct UrlRecord{
    #[sqlx(default)]
    id: String,
    #[sqlx(default)]
    url: String,
}

// 里面的pgPool 本身就是实现了Arc， 所以可以用Clone
#[derive(Debug, Clone)]
struct AppState{
    db: PgPool,
}


const LISTEN_ADDR: &str = "127.0.0.1:9876";

#[tokio::main]
async fn main() -> Result<()>{
    let layer = Layer::new().pretty().with_filter(LevelFilter::INFO);
    tracing_subscriber::registry().with(layer).init();

    let pg_url = "postgres://admin:qwk*7sk@localhost:5432/mydb";
    // 为什么要用 arc?  因为url 每次使用的时候 都会被 clone 一次， 这样相当于自动clone， 不用
   //let state = Arc::new(AppState::try_new(url).await?);
   // 或者本向就有 clone  也可以不用 Arc
   let state = AppState::try_new(pg_url).await?;
    info!("Connected to database: {pg_url}");

    //let addr = "0.0.0.0:9876";
    let listener  = TcpListener::bind(LISTEN_ADDR).await?;
    info!("Listening on: {}", LISTEN_ADDR);

    let app =  Router::new()
    .route("/", post(shorten))
    .route("/{id}", get(redirect))
    .with_state(state);

    axum::serve(listener, app.into_make_service()).await?;  
    

    Ok(())

}

// 这2个参数的顺序不能反， ， 否则就会报错。 原因  老师解释了， 但还是不太懂， 先这么写 , 说是 request 会分割成多个 extractor 什么的。 
async fn shorten( State(state) : State<AppState>,Json(data) : Json<ShortenReq>)   ->//   &'static str {
  Result< impl IntoResponse,  StatusCode> {
    let id = state
    .create(&data.url)
    .await
    .map_err(  |_| StatusCode::UNPROCESSABLE_ENTITY)?;

    let body =  Json(ShortenRes{
        url: format!("http://{}/{}", LISTEN_ADDR,id),
    });

    Ok((StatusCode::CREATED, body))
}

async fn redirect(Path(id): Path<String>, State(state) : State<AppState>,) ->// &'static str {
Result<impl IntoResponse, StatusCode>{
    let url = state
    .get_url(&id)
    .await
    .map_err(  |_| StatusCode::UNPROCESSABLE_ENTITY)?;
// 如果没有， 找不到呢， 只报一个错？  总感觉  错误 类型太少， 有可能表是不存在的。 这个处理的不太丰富呀。

    let  mut headers = HeaderMap::new();
    // let aa = url.parse();
    // info!("url.parse() => {}", aa.unwrap());
    headers.insert(LOCATION, url.parse().unwrap()); // 这个url.parse()  就是发出请求了？ 不太好读呀, 这个LOCATION 是个什么东西
    Ok((StatusCode::FOUND,headers))
}

impl AppState {
   async fn  try_new(url: &str) -> Result<Self> {
        let pool = PgPool::connect(url).await?;

        sqlx::query(
            r#"
            CREATE TABLE IF NOT EXISTS urls (
                id CHAR(6) PRIMARY KEY,
                url TEXT NOT NULL UNIQUE 
            )"#,
        )
        .execute(&pool)
        .await?;
    Ok(Self {db:pool})
    }


    async fn create(&self, url: &str) -> Result<String> {
            let id = nanoid!(6);

            info!("before  create:  id: {}, url: {} ", &id,url);
            let ret: UrlRecord = sqlx::query_as(
                     "INSERT INTO urls (id, url) VALUES ($1,$2)  ON CONFLICT(url)  DO UPDATE SET url=EXCLUDED.url RETURNING id",
                   )
            .bind(&id)
            .bind(url)
            .fetch_one(&self.db)
            .await?;

            info!("after  create:  id: {}, url: {} ", ret.id, ret.url);
        Ok(ret.id)
    }

    async fn get_url(&self, id: &str) -> Result<String> {
        let ret : UrlRecord= sqlx::query_as("SELECT url from urls WHERE ID = $1")
        .bind(id)
        .fetch_one(&self.db)
        .await?;

    Ok(ret.url)
    }
}

